﻿using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Xml.Linq;
using System.Collections.Generic;
using System.Text;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using LCA.Community.DAL.Model;

namespace LCA.Community.Web.Helpers
{
    /// <summary>
    /// Well-formed XHTML Calendar for ASP.NET MVC
    /// </summary>
    public class Calendar
    {
        /// <summary>
        /// Gets the name of current calendar, it's diaplayed as the "id" of the HTML element "table"
        /// </summary>
        public string Name { get; private set; }

        /// <summary>
        /// Gets the selected date of current calendar
        /// </summary>
        public DateTime SelectedDate { get; private set; }

        /// <summary>
        /// Gets the date array which to generate the links
        /// </summary>
        public IEnumerable<Event> RawDates { get; private set; }

        /// <summary>
        /// Gets the controller name that to generate the daily links
        /// </summary>
        public string ControllerName { get; private set; }

        /// <summary>
        /// Gets the action name that to generate the daily links
        /// </summary>
        public string ActionName { get; private set; }

        private HtmlHelper helper;

        /// <summary>
        /// Default calendar constructor
        /// </summary>
        public Calendar()
        {
            this.SelectedDate = DateTime.Now;
            this.Name = "MvcCalendar";
        }

        /// <summary>
        /// Create a calendar use the specified name
        /// </summary>
        /// <param name="name">the name of calendar</param>
        public Calendar(string name)
            : this()
        {
            this.Name = name;
        }

        /// <summary>
        /// Create a calendar use the specified name and selected date
        /// </summary>
        /// <param name="name">the name of calendar</param>
        /// <param name="selectedDate">selected date</param>
        public Calendar(string name, DateTime selectedDate)
            : this(name)
        {
            this.SelectedDate = selectedDate;
        }

        /// <summary>
        /// Create a calendar use data that generate links
        /// </summary>
        /// <param name="name">the name of calendar</param>
        /// <param name="selectedDate">selected date</param>
        /// <param name="rawDates">the date array</param>
        /// <param name="helper">HtmlHelper object</param>
        /// <param name="controllerName">controller name</param>
        /// <param name="actionName">action name</param>
        public Calendar(string name, DateTime selectedDate, IEnumerable<Event> rawDates, HtmlHelper helper, string controllerName, string actionName)
            : this(name)
        {
            this.SelectedDate = selectedDate;
            this.RawDates = rawDates;
            this.helper = helper;
            this.ControllerName = controllerName;
            this.ActionName = actionName;
        }

        /// <summary>
        /// Generates the header.
        /// </summary>
        /// <returns></returns>
        private string GenerateHeader()
        {
            MvcHtmlString prevLink = MvcHtmlString.Empty;
            MvcHtmlString nextLink = MvcHtmlString.Empty;

            var header = new StringBuilder();
            header.AppendFormat("<table cellSpacing=\"0\">", this.Name);

            if (this.helper != null && this.RawDates != null && this.RawDates.ToList().Count > 0)
            {
                bool hasPrev = false;
                bool hasNext = false;

                var rawDates = from e in RawDates
                               select e.Date;

                string groupID = RawDates.First().PartitionKey;

                foreach (var date in rawDates)
                {
                    if (date.Year == SelectedDate.Year && date.Month < SelectedDate.Month) hasPrev = true;
                    if (date.Year == SelectedDate.Year && date.Month > SelectedDate.Month) hasNext = true;
                }

                if (hasPrev) prevLink = System.Web.Mvc.Ajax.AjaxExtensions.ActionLink(new AjaxHelper(helper.ViewContext, helper.ViewDataContainer), "<<", "ShowMonth", "Events", new { id = groupID, month = SelectedDate.AddMonths(-1).Month, year = SelectedDate.AddMonths(1).Year }, new System.Web.Mvc.Ajax.AjaxOptions { UpdateTargetId = "calendar", InsertionMode = System.Web.Mvc.Ajax.InsertionMode.Replace,  OnSuccess = "SetupEvents" });
                if (hasNext) nextLink = System.Web.Mvc.Ajax.AjaxExtensions.ActionLink(new AjaxHelper(helper.ViewContext, helper.ViewDataContainer), ">>", "ShowMonth", "Events", new { id = groupID, month = SelectedDate.AddMonths(1).Month, year = SelectedDate.AddMonths(1).Year }, new System.Web.Mvc.Ajax.AjaxOptions { UpdateTargetId = "calendar", InsertionMode = System.Web.Mvc.Ajax.InsertionMode.Replace, OnSuccess = "SetupEvents" });
                //if (hasPrev) prevLink = helper.ActionLink("<<", this.ActionName, this.ControllerName, new { date = string.Format("{0}-{1}", SelectedDate.AddMonths(-1).Year, SelectedDate.AddMonths(-1).Month) });
                //if (hasNext) nextLink = helper.ActionLink(">>", this.ActionName, this.ControllerName, new { date = string.Format("{0}-{1}", SelectedDate.AddMonths(1).Year, SelectedDate.AddMonths(1).Month) });
            }
            string headerText = this.SelectedDate.ToString("y");
            header.AppendFormat("<caption>{0} {1} {2}</caption>", prevLink, headerText, nextLink);
            
            header.Append("<thead><tr>");

            //header.Append("<th scope=\"col\" abbr=\"Sunday\" title=\"Sunday\">Sun</th>");
            //header.Append("<th scope=\"col\" abbr=\"Monday\" title=\"Monday\">Mon</th>");
            //header.Append("<th scope=\"col\" abbr=\"Tuesday\" title=\"Tuesday\">Tue</th>");
            //header.Append("<th scope=\"col\" abbr=\"Wednesday\" title=\"Wednesday\">Wed</th>");
            //header.Append("<th scope=\"col\" abbr=\"Thursday\" title=\"Thursday\">Thu</th>");
            //header.Append("<th scope=\"col\" abbr=\"Friday\" title=\"Friday\">Fri</th>");
            //header.Append("<th scope=\"col\" abbr=\"Saturday\" title=\"Saturday\">Sat</th>");

            header.Append("<th>L</th>");
            header.Append("<th>M</th>");
            header.Append("<th>X</th>");
            header.Append("<th>J</th>");
            header.Append("<th>V</th>");
            header.Append("<th>S</th>");
            header.Append("<th>D</th>");

            //header.Append("<th scope=\"col\" abbr=\"星期日\" title=\"星期日\">日</th>");
            //header.Append("<th scope=\"col\" abbr=\"星期一\" title=\"星期一\">一</th>");
            //header.Append("<th scope=\"col\" abbr=\"星期二\" title=\"星期二\">二</th>");
            //header.Append("<th scope=\"col\" abbr=\"星期三\" title=\"星期三\">三</th>");
            //header.Append("<th scope=\"col\" abbr=\"星期四\" title=\"星期四\">四</th>");
            //header.Append("<th scope=\"col\" abbr=\"星期五\" title=\"星期五\">五</th>");
            //header.Append("<th scope=\"col\" abbr=\"星期六\" title=\"星期六\">六</th>");

            header.Append("</tr></thead>");

            return header.ToString();
        }

        /// <summary>
        /// Generates the footer.
        /// </summary>
        /// <returns></returns>
        private string GenerateFooter()
        {
            var footer = new StringBuilder();
            footer.Append("<tfoot><tr>");
            footer.Append("<th>L</th>");
            footer.Append("<th>M</th>");
            footer.Append("<th>X</th>");
            footer.Append("<th>J</th>");
            footer.Append("<th>V</th>");
            footer.Append("<th>S</th>");
            footer.Append("<th>D</th>");
            footer.Append("</tr></tfoot></table>");
            return footer.ToString();
        }

        /// <summary>
        /// Generates the days.
        /// </summary>
        /// <returns></returns>
        private string GenerateDays()
        {
            var now = DateTime.Now;
            var date = new DateTime(this.SelectedDate.Year, this.SelectedDate.Month, 1);
            var days = DateTime.DaysInMonth(this.SelectedDate.Year, this.SelectedDate.Month);
            var weekDayOfFirstDay = (int)date.DayOfWeek;

            var daysXhtml = new StringBuilder();

            int day = 1;//first day of the date's month
            int weekDay = 1;//0 - 6

            if (weekDayOfFirstDay != 1)
            {
                daysXhtml.Append("<tr>");
                while (weekDay != weekDayOfFirstDay)
                {
                    daysXhtml.Append("<td>&nbsp;</td>");
                    weekDay = ((weekDay + 1) % 7);
                }
            }

            while (day <= days)
            {
                if (weekDay == 1) daysXhtml.Append("<tr>");

                if (date.Year == now.Year && date.Month == now.Month && day == now.Day)
                    daysXhtml.Append("<td class=\"today\">" + day + "</td>");
                else
                    daysXhtml.Append("<td>" + day + "</td>");

                if (weekDay == 0)
                {
                    daysXhtml.Append("</tr>");
                }

                day++;
                weekDay++;
            }

            if (weekDay != 1)
            {
                while (weekDay != 1)
                {
                    daysXhtml.Append("<td>&nbsp;</td>");
                    weekDay = ((weekDay + 1) % 7);
                }
                daysXhtml.Append("</tr>");
            }

            return daysXhtml.ToString();
        }

        /// <summary>
        /// Generates the days.
        /// </summary>
        /// <param name="rawDates">The raw dates.</param>
        /// <returns></returns>
        private string GenerateDays(IEnumerable<Event> rawDates)
        {
            var now = DateTime.Now;
            var date = new DateTime(this.SelectedDate.Year, this.SelectedDate.Month, 1);
            var days = DateTime.DaysInMonth(this.SelectedDate.Year, this.SelectedDate.Month);
            var weekDayOfFirstDay = (int)date.DayOfWeek;

            var daysXhtml = new StringBuilder();

            int day = 1;//first day of the date's month
            int weekDay = 1;//0 - 6

            if (weekDayOfFirstDay != 1)
            {
                daysXhtml.Append("<tr>");
                while (weekDay != weekDayOfFirstDay)
                {
                    daysXhtml.Append("<td>&nbsp;</td>");
                    weekDay = ((weekDay + 1) % 7);
                }
            }

            while (day <= days)
            {
                StringBuilder dayString = new StringBuilder();

                if (weekDay == 1) daysXhtml.Append("<tr>");

                var caldate = new DateTime(date.Year, date.Month, day);
                IEnumerable<Event> events = from d in rawDates
                                            where d.Date.Date == caldate.Date
                                            select d;

                if (events.Count() > 0)
                {
                    if (date.Year == now.Year && date.Month == now.Month && day == now.Day)
                        dayString.Append("<td class=\"today date_has_event\">");
                    else
                        daysXhtml.Append("<td class=\"date_has_event\">");

                    dayString.Append(day+"<DIV class=\"events\"><UL>");
                    foreach (Event e in events)
                    {
                        dayString.Append(String.Format("<LI><SPAN class=\"title\">{0}</SPAN> <SPAN class=\"desc\">{1}</SPAN></LI>", e.Name, e.Description));
                        //dayString = helper.ActionLink(day.ToString(), ActionName, ControllerName, new { date = string.Format("{0}-{1}-{2}", caldate.Year, caldate.Month, caldate.Day) });
                    }
                    dayString.Append("</UL></DIV></td>");
                }
                else
                {
                    if (date.Year == now.Year && date.Month == now.Month && day == now.Day)
                        dayString.Append("<td class=\"today\">");
                    else
                        daysXhtml.Append("<td>");
                    dayString.Append(day+"</td>");
                }

                daysXhtml.Append(dayString.ToString());

                if (weekDay == 0)
                {
                    daysXhtml.Append("</tr>");
                }

                day++;
                weekDay = ((weekDay + 1) % 7);
            }

            if (weekDay != 1)
            {
                while (weekDay != 1)
                {
                    daysXhtml.Append("<td>&nbsp;</td>");
                    weekDay = ((weekDay + 1) % 7);
                }
                daysXhtml.Append("</tr>");
            }

            return daysXhtml.ToString();
        }

        /// <summary>
        /// Returns the built calendar
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            var calendar = new StringBuilder();

            if (this.RawDates != null)
            {
                //Build a calendar with links
                calendar.AppendFormat("{0}{1}{2}",
                    GenerateHeader(),
                    GenerateDays(RawDates),
                    GenerateFooter()
                    );
            }
            else
            {
                //build an empty calendar for current month
                calendar.AppendFormat("{0}{1}{2}",
                    GenerateHeader(),
                    GenerateDays(),
                    GenerateFooter()
                    );
            }

            return calendar.ToString();
        }
    }

}